package com.yang.framework;

import com.yang.annotation.Api;
import com.yang.annotation.ApiMapping;
import com.yang.utils.AopTargetUtils;
import com.yang.utils.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.map.HashedMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.el.util.ReflectionUtil;
import org.apache.tomcat.jni.Error;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * @Description: 扩展bean声明周期的接口实现类
 *                  具体的功能就是我们想在容器启动完成后, 得到一个数据map
 *                  map的key就是可以定位到唯一的方法
 *                  map的value就是把这个解析为我们想要的数据对象(对象有方法的参数, 方法的属性, 方法的描述, 方法的执行等等等)
 * @author: Yang Qiang
 * @create: 2021-11-11 22:31
 */
@Slf4j
@Component
public class YangBeanPostProcessors implements BeanPostProcessor {

    // key:指定类的指定方法路径   value:方法对应的业务
    private final Map<String, ApiMethodInfo> apiMethodInfoMap = new HashedMap();

    /**
     * 重写, bean初始化之后调用的方法
     * 也就是bean的声明周期走完了, 我们就开始进行扩展, 对bean进行一些相应的处理
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        // 从容器中获取被我们标注的业务类
        Class<?> clazz = bean.getClass();
        Api api = getApi(clazz);
        if (api == null){
            return bean;
        }

        // 如果是我们自己的类
        try {
            // 获取原生类, 因为有可能是获取到的代理类
            Object target = AopTargetUtils.getTarget(bean);
            // 得到类上所有的方法
            Method[] methods = ReflectionUtils.getAllDeclaredMethods(target.getClass());
            // 循环方法解析为我们的APIMethod对象
            for (Method method : methods) {
                putContext(method, beanName, api);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    private void putContext(Method method, String beanName, Api api) {
        // 获取方法上面的ApiMapping注解
        ApiMapping apiMapping = AnnotationUtils.findAnnotation(method, ApiMapping.class);
        if (apiMapping == null){
            return;
        }

        // 获取注解对方法的描述
        String memo = apiMapping.memo();
        // 获取方法名字
        String methodName = apiMapping.method();
        // 获取方法的路径
        String valueMethod = apiMapping.value();
        // 获取类注解的路径
        String valueClass = api.value();
        String value = StringUtils.isNotBlank(valueMethod) ? valueMethod : valueClass;
        // 拼接相应的key, 就是有个key可以对应到唯一的方法
        String key = getKey(value, methodName);
        // 然后进行组合我们需要的map信息
        ApiMethodInfo apiMethodInfo = apiMethodInfoMap.get(key);
        // 没有的话再进行解析
        if (apiMethodInfo != null){
            // 获取map里面有的方法的类
            Class<?> cacheClass = apiMethodInfo.getMethod().getDeclaringClass();
            // 获取需要解析方法的类
            Class<?> declaringClass = method.getDeclaringClass();
            // 如果相等就返回
            if (cacheClass == declaringClass){
                return;
            }else {
                throw new RuntimeException(key + "接口重复注册");
            }
        }

        // 如果map里面没有我们再进行解析
        ApiMethodInfo apiMethodInfo1 = new ApiMethodInfo();
        apiMethodInfo1.setMethod(method);
        apiMethodInfo1.setDesc(memo);
        apiMethodInfo1.setBeanName(beanName);
        apiMethodInfo1.buildParameters();
        // 注册到map中
        apiMethodInfoMap.put(key, apiMethodInfo1);
    }

    private String getKey(String value, String methodName) {
        return StringUtil.join("_", value, methodName);
    }

    // 判断类上是否有@Api, 注解, 就是判断是不是哦我们需要的业务类
    private Api getApi(Class<?> clazz) {
        Annotation[] annotations = clazz.getAnnotations();
        if (annotations == null || annotations.length == 0){
            return null;
        }
        for (Annotation annotation : annotations) {
            if (annotation instanceof Api){
                return (Api) annotation;
            }
        }
        return null;
    }

    /**
     * 得到唯一的方法
     * @param command
     * @param method
     * @return
     */
    public ApiMethodInfo getApiMethod(String command, String method){
        String key = getKey(command, method);
        key = "/get/act_apiTest";
        return apiMethodInfoMap.get(key);
    }
}
